package com.zhx.common.common.util.discover;


import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.zhx.common.common.util.discover.doamin.UrlInfo;

import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.data.util.Pair;

import javax.annotation.Nullable;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public abstract class AbstractUrlDiscover implements UrlDiscover {

    //链接识别的正则
    private static final Pattern PATTERN = Pattern.compile("((http|https)://)?(www.)?([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?");


    /**
     * 通过CompletableFuture异步处理多个任务，并将所有任务的结果转换为一个Map<String, UrlInfo>，其中如果有键重复的情况，会保留最先处理的值。
     * key 为 url，value为解析出来的值
     * @param content
     * @return
     */
    @Nullable
    @Override
    public Map<String, UrlInfo> getUrlContentMap(String content) {
        if (StrUtil.isBlank(content)) {
            return new HashMap<>();
        }
        //使用正则表达式匹配所有Url
        List<String> macheList = ReUtil.findAll(PATTERN, content, 0);

        // 使用并行流处理匹配到的每个URL，异步获取其内容信息
        List<CompletableFuture<Pair<String, UrlInfo>>> futures = macheList.stream().map(url -> CompletableFuture.supplyAsync(() -> {
            UrlInfo urlInfo = getContent(url);
            return Objects.isNull(urlInfo) ? null : Pair.of(url, urlInfo);
        })).collect(Collectors.toList());

        // 将所有CompletableFuture组合成一个CompletableFuture列表，并等待所有异步任务完成

        // 通过CompletableFuture.allOf()方法来等待所有的CompletableFuture完成。
        // 使用thenApply方法来对之前等待完成的CompletableFuture进行结果转换。
        // 这里通过stream、map、filter和collect等方法，将每个CompletableFuture的处理结果转换为一个非空的List<Pair<String, UrlInfo>>。
        CompletableFuture<List<Pair<String, UrlInfo>>> future = CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]))
                .thenApply(v -> futures.stream()
                        .map(CompletableFuture::join)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList())
                );

        // 将完成的任务结果转换为Map，如果有键重复的情况，保留最先处理的值
        return future.join().stream().collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, (a, b) -> a));
    }


    @Nullable
    @Override
    public UrlInfo getContent(String url) {
        Document document = getUrlDocument(assemble(url));
        if (Objects.isNull(document)) {
            return null;
        }
        return UrlInfo.builder()
                .title(getTitle(document))
                .description(getDescription(document))
                .image(getImage(assemble(url), document))
                .build();
    }

    /**
     * 组装完整的URL地址。
     * 如果传入的URL不是以"http"开头，则在URL前添加"http://"确保URL的完整性。
     *
     * @param url 待检查和组装的URL字符串。
     * @return 组装后的完整URL字符串。
     */
    private String assemble(String url) {
        // 检查URL是否以"http"开头，如果不是，则添加"http://"确保URL格式正确
        if (!StrUtil.startWith(url, "http")) {
            return "http://" + url;
        }
        return url;
    }

    protected Document getUrlDocument(String url) {
        try {
            Connection connect = Jsoup.connect(url);
            connect.timeout(2000);
            return connect.get();
        } catch (IOException e) {
            log.error("find error:url:{}", url, e);
        }
        return null;
    }


    /**
     * 判断链接是否有效
     * 输入链接
     * 返回true或者false
     */
    public static boolean isConnect(String href) {
        //请求地址
        URL url;
        //请求状态码
        int state;
        //下载链接类型
        String fileType;
        try {
            url = new URL(href);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            state = httpURLConnection.getResponseCode();
            fileType = httpURLConnection.getHeaderField("Content-Disposition");
            //如果成功200，缓存304，移动302都算有效链接，并且不是下载链接
            if ((state == 200 || state == 302 || state == 304) && fileType == null) {
                return true;
            }
            httpURLConnection.disconnect();
        } catch (Exception e) {
            return false;
        }
        return false;
    }


}
